part of internal;

class Buffer {
   DoublyLinkedList<String> _raw;
   PriorityQueue<int> _pq;
   int _cc;
   DoublyLinkedList<Line> _rend;

   // generator
   Buffer () {
      _raw = new DoublyLinkedList<String>();
      _raw.insert(0, '');
      _pq = new PriorityQueue<int>(fastLookup : true);
      _pq.add(0);
      _cc = 0;
      _rend = new DoublyLinkedList<Line>();
      _rend.insert(0, new Line(''));
   }

   // mutators
   Position insert (String seq, Position pos) {
      if (dev) {
         if (pos.ln >= _raw.len) argErr(pos);
         if (pos.ch > _raw.nth(pos.ln).length) argErr(pos);
      }

      // extract left and right
      String full = _raw.nth(pos.ln);
      String left = full.substring(0, pos.ch);
      String right = full.substring(pos.ch);

      // essentially string.split
      Position ret_pos = new Position(pos.ln, pos.ch);
      int ln_num = pos.ln;
      int from = 0;
      for (int i = 0; i < seq.length; i++) {
         if (seq[i] != '\n') continue;
         String tmp = seq.substring(from, i);
         _pq.remove(_raw.nth(ln_num).length);
         _raw.replace(ln_num, left + tmp);
         _pq.add(_raw.nth(ln_num).length);
         _rend.nth(ln_num).let = _raw.nth(ln_num);
         ln_num++;
         _raw.insert(ln_num, '');
         _pq.add(0);
         _rend.insert(ln_num, new Line(''));
         from = i + 1;
         left = '';
         ret_pos.ln++;
         ret_pos.ch = 0;
      }
      String tmp = seq.substring(from);
      _pq.remove(_raw.nth(ln_num).length);
      _raw.replace(ln_num, left + tmp + right);
      _pq.add(_raw.nth(ln_num).length);
      _rend.nth(ln_num).let = _raw.nth(ln_num);
      ret_pos.ch += tmp.length;

      // simple stuff
      _cc += seq.length;
      return ret_pos;
   }
   Position delete (int cnt, Position pos) {
      if (dev) {
         if (pos.ln >= _raw.len) argErr(pos);
         if (pos.ch > _raw.nth(pos.ln).length) argErr(pos);
      }

      // extract left and right
      String full = _raw.nth(pos.ln);
      String left = full.substring(0, pos.ch);
      String right = full.substring(pos.ch);

      // essentially cut out in chunks
      Position ret_pos = new Position(pos.ln, pos.ch);
      int ln_num = pos.ln;
      int rem = cnt;
      while (true) {
         if (rem > left.length) {
            _pq.remove(_raw.nth(ln_num).length);
            _raw.delete(ln_num);
            _rend.delete(ln_num);
            rem -= (left.length + 1);
            ln_num--;
            left = _raw.nth(ln_num);
            ret_pos.ln--;
            ret_pos.ch = left.length;
         }
         else {
            String new_left = left.substring(0, left.length - rem);
            _pq.remove(_raw.nth(ln_num).length);
            _raw.replace(ln_num, new_left + right);
            _pq.add(_raw.nth(ln_num).length);
            _rend.nth(ln_num).let = _raw.nth(ln_num);
            ret_pos.ch -= rem;
            break;
         }
      }

      // simple stuff
      _cc -= cnt;
      return ret_pos;
   }

   // observers
   String read (int cnt, Position pos) {
      if (dev) {
         if (pos.ln >= _raw.len) argErr(pos);
         if (pos.ch > _raw.nth(pos.ln).length) argErr(pos);
      }
      StringBuffer seq = new StringBuffer('');
      int ln_num = pos.ln;
      int rem = cnt;
      String right = _raw.nth(pos.ln).substring(pos.ch);
      while (true) {
         if (rem > right.length) {
            seq.writeln(right);
            rem -= (right.length + 1);
            ln_num++;
            right = _raw.nth(ln_num);
         }
         else {
            seq.write(right.substring(0, rem));
            break;
         }
      }
      return seq.toString();
   }
   String nth (int n) {
      if (dev) if (n >= _raw.len) argErr(n);
      return _raw.nth(n);
   }
   int get lc => _raw.len;
   int get cc => _cc;
   int get width => _pq.peek();
   DoublyLinkedList<Line> get rend => _rend;
/*
   // invariants
   bool get valid {
      if (_raw == null) return false;
      if (_cc == null) return false;
      if (_cc < 0) return false;
      int tmp = 0;
      for (int i = 0; i < _raw.len; i++) {
         tmp += _raw.nth(i).length;
         if (i == _raw.len - 1);
         else tmp++;
      }
      if (tmp != _cc) return false;
      return true;
   }

   // printing
   String toString () {
      StringBuffer tmp = new StringBuffer();
      for (int i = 0; i < _raw.len; i++) {
         tmp.write(_raw.nth(i));
         if (i == _raw.len - 1);
         else tmp.writeln();
      }
      return tmp.toString();
   }
   */
}
